﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using Qreed.Drawing.Drawing2D;
using System.Diagnostics;
using System.Xml.Serialization;
using System.IO;

namespace Qreed.Drawing.TriDemo
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();

            canvas.MouseWheel += new MouseEventHandler(canvas_MouseWheel);
            AddNewPolygon();
        }

        
        protected override void OnLoad(EventArgs e)
        {
            try
            {
                shape = (Figure2D)DeserializeSetting("LastFigure2D", typeof(Figure2D)) ?? new Figure2D();
                polygon = (Polygon2D)DeserializeSetting("LastPolygon2D", typeof(Polygon2D)) ?? new Polygon2D();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error while loading settings.", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            
            DebugShape();

            if (shape != null)
            {
                RectangleF rect = shape.GetBounds();

                if (rect != RectangleF.Empty)
                {
                    canvas.PageSize = new Size((int)(rect.X + rect.Width), (int)(rect.Y + rect.Height));
                }
            }

            
            this.toolStripComboBoxRenderPolygon.Items.Clear();
            
            for(int i = -1; i < 1000; i++)
                this.toolStripComboBoxRenderPolygon.Items.Add(i);

            this.toolStripComboBoxRenderPolygon.SelectedIndex = 0;

            base.OnLoad(e);
        }

        private object DeserializeSetting(string propertyName, Type type)
        {
            string value = (string)Properties.Settings.Default[propertyName];

            if (!string.IsNullOrEmpty(value))
            {
                using (StringReader sr = new StringReader(value))
                {
                    XmlSerializer s = new XmlSerializer(type);

                    return s.Deserialize(sr);
                }
            }

            return null;
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            try
            {
                SerializeSetting("LastFigure2D", shape);
                SerializeSetting("LastPolygon2D", polygon);
            
                Properties.Settings.Default.Save();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error while saving settings.", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            base.OnClosing(e);
        }

        private void SerializeSetting(string propertyName, object value)
        {
            StringBuilder sb = new StringBuilder();

            using (StringWriter sw = new StringWriter(sb))
            {
                XmlSerializer s = new XmlSerializer(value.GetType());

                s.Serialize(sw, value);
            }

            Properties.Settings.Default[propertyName] = sb.ToString();
        }

        Polygon2D polygon = null;
        Figure2D shape = new Figure2D();

       
        private void DebugShape()
        {
            StringBuilder sb = new StringBuilder();

            Debug.WriteLine(string.Format("Shape contains {0} polygons.", shape.Polygons.Count));

            for (int i = 0; i < shape.Polygons.Count; i++)
            {
                Polygon2D poly = shape.Polygons[i];
                DebugPolygon(i, poly);
            }
        }

        private static void DebugPolygon(int i, Polygon2D poly)
        {
            if (i >= 0)
            {
                Debug.WriteLine(string.Format("[{0:000}] {1:0000} vertices {2} {3}", i, poly.Count, poly.SegmentType, poly.WindingOrder));
            }
            else
            {
                Debug.WriteLine(string.Format("{1:0000} vertices {2} {3}", i, poly.Count, poly.SegmentType, poly.WindingOrder));
            }
        }

        private void AddNewPolygon()
        {
            if (polygon != null)
            {
                shape.Add(polygon);
                DebugShape();
            }

            polygon = new Polygon2D();
        }

        private void AddPoint(PointF point)
        {
            Debug.WriteLine(string.Format("Adding point {0} {1}", point.X, point.Y));
            polygon.Vertices.Add(point);

            DebugPolygon(-1, polygon);
            canvas.Invalidate();
        }

        private void AddPoint(Point point)
        {
            AddPoint((PointF)point);            
        }

        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            try
            {
                /*Matrix mm = e.Graphics.Transform;

                mm.Translate(0.0f, canvas.PageSize.Height);
                mm.Scale(1.0f, -1.0f);

                e.Graphics.Transform = mm;

                //e.Graphics.FillEllipse(Brushes.Black, 0, 0 , 50, 50);
                //e.Graphics.FillEllipse(Brushes.Black, 0, 500, 50, 50);
                //e.Graphics.FillEllipse(Brushes.Black, 500, 500, 50, 50);
                //e.Graphics.FillEllipse(Brushes.Black, 500, 0, 50, 50);
                */

                Matrix m = GetTransformMatrix();
                m.Multiply(e.Graphics.Transform, MatrixOrder.Append);

                e.Graphics.Transform = m;

                List<PointF> allReadyDone = new List<PointF>();
            
                if (shape != null)
                {
                    if (toolStripComboBoxRenderPolygon.SelectedIndex != -1 && ((int)toolStripComboBoxRenderPolygon.SelectedItem != -1))
                    {
                        int i = (int)toolStripComboBoxRenderPolygon.SelectedItem;

                        if (i < shape.Polygons.Count)
                            DrawPolygon(e.Graphics, shape.Polygons[i], new SolidBrush(Color.FromArgb(0x30, Color.Black)), Pens.Black, Pens.Gray, Brushes.Black, allReadyDone);
                    }
                    else
                    {
                        foreach (Polygon2D poly in shape.Polygons)
                            DrawPolygon(e.Graphics, poly, new SolidBrush(Color.FromArgb(0x30, Color.Black)), Pens.Black, Pens.Gray, Brushes.Black, allReadyDone);
                    }
                }

                if (this.polygon != null)
                    DrawPolygon(e.Graphics, polygon, Brushes.LightBlue, Pens.Blue, Pens.BlueViolet, Brushes.Blue, allReadyDone);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
            }
        }

        private void DrawPolygon(Graphics g, Polygon2D polygon, Brush fillBrush, Pen outlinePen, Pen linePen, Brush pointBrush, List<PointF> allReadyDone)
        {
            PointF[] points = polygon.Vertices.ToArray();

            if (points.Length >= 3)
            {
                g.FillPolygon(fillBrush, points);
                g.DrawPolygon(linePen, points);
            }

            // Draw the dots

            for (int i = 0; i < points.Length; i++)
            {
                PointF pt = points[i];

                g.FillEllipse(pointBrush, new RectangleF(pt.X - 4.0f, pt.Y - 4.0f, 8.0f, 8.0f));

                bool b = false;

                foreach (PointF pt2 in allReadyDone)
                {
                    if (pt.X == pt2.X && pt.Y == pt2.Y)
                    {
                        b = true;
                        break;
                    }
                }

                float y = pt.Y + 15.0f;

                if (b)
                    y += 20.0f;
                else
                    allReadyDone.Add(pt);

                PointF textPos = new PointF(pt.X - 15.0f, y);

                textPos = CanvasPointToClient(textPos);
                
                GraphicsState gs = g.Save();
                g.ResetTransform();
                g.DrawString(i.ToString(), Font, pointBrush, textPos);
                g.Restore(gs);
            }
        }

        private void toolStripButtonClick(object sender, EventArgs e)
        {
            if (sender == toolStripButtonAdd)
                Form2_KeyDown(sender, new KeyEventArgs(Keys.Enter));
            else if(sender == toolStripButtonClear)
                Form2_KeyDown(sender, new KeyEventArgs(Keys.Escape));
            else if (sender == toolStripButtonSubstract)
                Form2_KeyDown(sender, new KeyEventArgs(Keys.Delete));
        }

        private void Form2_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyData)
            {
                case Keys.Escape:
                    polygon = new Polygon2D(); ;
                    shape = new Figure2D();

                    canvas.PageSize = new Size(400, 400);
                    //polygon.Vertices.Add(new PointF(0.0f, 0.0f));
                    polygon.Vertices.Add(new PointF(0.0f, 357.910156f));
                    polygon.Vertices.Add(new PointF(48.78235f, 357.910156f));
                    polygon.Vertices.Add(new PointF(48.78235f, 210.9375f));
                    polygon.Vertices.Add(new PointF(48.78235f, 168.701172f));
                    polygon.Vertices.Add(new PointF(48.78235f, 0.0f));

                    AddNewPolygon();

                    canvas.Invalidate();
                    break;

                case Keys.Enter:
                    AddNewPolygon();
                    canvas.Invalidate();
                    break;

                case Keys.Delete:
                    if (polygon != null)
                    {
                        shape.Substract(polygon);
                        polygon = new Polygon2D();
                    }
                    canvas.Invalidate();
                    break;

                case Keys.F:
                    if (shape != null)
                        shape.Triangulate();
                    canvas.Invalidate();
                    break;
            }

            DebugShape();
        }

        private void toolStripButtonText_Click(object sender, EventArgs e)
        {
            AddTextForm dlg = new AddTextForm();

            if (dlg.ShowDialog(this) == DialogResult.OK)
            {
                polygon = new Polygon2D();

                GraphicsPath gp = new GraphicsPath();

                FontFamily ff = new FontFamily(dlg.FontName);
                FontStyle fs = (FontStyle)Enum.Parse(typeof(FontStyle), dlg.FontStyle);

                StringFormat sf = new StringFormat(StringFormat.GenericDefault);
                //sf.Alignment = StringAlignment.;
                //sf.LineAlignment = StringAlignment.Far;
                
                gp.AddString(dlg.TextValue, ff, (int)fs, 1.0f, new Point(0, 0), sf);

                Matrix m = new Matrix();
                m.Scale(dlg.ScaleValue, dlg.ScaleValue);
                //m.Scale(1.0f, -1.0f);
                m.Multiply(GetTransformMatrix(), MatrixOrder.Append);

                gp.Flatten(m, dlg.Flatten);

                RectangleF bounds = gp.GetBounds();
                canvas.PageSize = bounds.Size.ToSize();

                Matrix m2 = new Matrix();
                m2.Translate(-bounds.Left, -bounds.Top);

                gp.Transform(m2);

                shape = new Figure2D(gp);

                
                //canvas.Invalidate();
            }
        }

        private void toolStripButtonAddPoint_Click(object sender, EventArgs e)
        {
            AddPointForm dlg = new AddPointForm();

            if (dlg.ShowDialog(this) == DialogResult.OK)
            {
                this.AddPoint(dlg.Point);
            }
        }

        private void toolStripComboBoxRenderPolygon_SelectedIndexChanged(object sender, EventArgs e)
        {
            canvas.Invalidate();
        }

        private Matrix GetTransformMatrix()
        {
            Matrix m = new Matrix();

            //m.Translate(-canvas.PageSize.Width * 0.5f, canvas.PageSize.Height * 0.5f);
            m.Translate(0.0f, canvas.PageSize.Height);
            m.Scale(1.0f, -1.0f);

            return m;
        }

        private PointF CanvasPointToClient(PointF pt)
        {
            Matrix m = GetTransformMatrix();
            PointF[] px = new PointF[] { pt };
            m.TransformPoints(px);

            return canvas.CanvasPointToClient(px[0]);
        }

        private Point ClientPointToCanvas(Point p)
        {
            PointF ptf = new PointF(p.X, p.Y);
            ptf = ClientPointToCanvas(ptf);
            return new Point((int)ptf.X, (int)ptf.Y);
        }

        private PointF ClientPointToCanvas(PointF p)
        {
            p = canvas.ClientPointToCanvas(p);

            Matrix m = GetTransformMatrix();
            m.Invert();

            PointF[] pts = new PointF[] { p };

            m.TransformPoints(pts);

            return pts[0];
        }

        private void canvas_MouseMove(object sender, MouseEventArgs e)
        {
            Point pt = ClientPointToCanvas(e.Location);         
            this.Text = string.Format("X: {0:0000} Y: {1:0000}", pt.X, pt.Y);
        }

        private void panel1_MouseClick(object sender, MouseEventArgs e)
        {
            //Debug.WriteLine(string.Format("Click: {0} {1}", e.Location.X, e.Location.Y));
            
            Point pt = ClientPointToCanvas(e.Location);
            AddPoint(pt);
        }

        void canvas_MouseWheel(object sender, MouseEventArgs e)
        {
            //canvas.Zoom += e.Delta * 0.001f;
        }

    }
}
